package com.ctg.itrdc.sysmgr.permission.core.realm;

import java.util.HashSet;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasAuthenticationException;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.StringUtils;
import org.jasig.cas.client.authentication.AttributePrincipal;
import org.jasig.cas.client.validation.Assertion;
import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ctg.itrdc.sysmgr.permission.core.CtgUser;
import com.ctg.itrdc.sysmgr.permission.core.IPermissionService;

public class CtgCasRealm extends CasRealm {

	private static Logger log = LoggerFactory.getLogger(CtgCasRealm.class);

	@Resource
	private IPermissionService permissionService;

	public IPermissionService getPermissionService() {
		return permissionService;
	}

	public void setPermissionService(IPermissionService permissionService) {
		this.permissionService = permissionService;
	}
	
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {

		// 因为非正常退出，即没有显式调用 SecurityUtils.getSubject().logout()
		// (可能是关闭浏览器，或超时)，但此时缓存依旧存在(principals)，所以会自己跑到授权方法里。
		if (!SecurityUtils.getSubject().isAuthenticated()) {
			doClearCache(principals);
			SecurityUtils.getSubject().logout();
			return null;
		}

		CtgUser ctgUser = (CtgUser) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

		Long sysPostId = ctgUser.getSysPostId();
		Long sysUserId = ctgUser.getSysUserId();

		List<String> permissionList = permissionService.getPermissions(
				sysUserId, sysPostId);
		if (permissionList != null && permissionList.size() > 0) {
			authorizationInfo.setStringPermissions(new HashSet<String>(permissionList));
		}

		List<String> rolesNameList = permissionService.getRoleCodes(sysUserId,
				sysPostId);
		if (rolesNameList != null && rolesNameList.size() > 0) {
			authorizationInfo.setRoles(new HashSet<String>(rolesNameList));
		}

		return authorizationInfo;
	}

	/**
	 * Authenticates a user and retrieves its information.
	 * 
	 * @param token
	 *            the authentication token
	 * @throws AuthenticationException
	 *             if there is an error during authentication.
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {

		CasToken casToken = (CasToken) token;
		if (token == null) {
			return null;
		}

		String ticket = (String) casToken.getCredentials();
		if (!StringUtils.hasText(ticket)) {
			return null;
		}

		TicketValidator ticketValidator = ensureTicketValidator();

		try {
			// contact CAS server to validate service ticket
			Assertion casAssertion = ticketValidator.validate(ticket,
					getCasService());
			// get principal, user id and attributes
			AttributePrincipal casPrincipal = casAssertion.getPrincipal();
			String sysUserCode = casPrincipal.getName();
			log.debug(
					"Validate ticket : {} in CAS server : {} to retrieve sysUserCode : {}",
					new Object[] { ticket, getCasServerUrlPrefix(), sysUserCode });

			Map<String, Object> attributes = casPrincipal.getAttributes();

			Long sysUserId = attributes.get("userId") == null ? null : Long
					.parseLong(attributes.get("userId").toString());
			Long staffId = attributes.get("staffId") == null ? null : Long
					.parseLong(attributes.get("staffId").toString());

			if (sysUserId == null || staffId == null) {
				Map<String, Object> user = permissionService
						.getUserByUsername(sysUserCode);
				if (sysUserId == null)
					sysUserId = (Long) user.get("SYS_USER_ID");
				if (staffId == null)
					staffId = (Long) user.get("STAFF_ID");
			}

			// refresh authentication token (user id + remember me)
			casToken.setUserId(sysUserCode);
			String rememberMeAttributeName = getRememberMeAttributeName();
			String rememberMeStringValue = (String) attributes
					.get(rememberMeAttributeName);
			boolean isRemembered = rememberMeStringValue != null
					&& Boolean.parseBoolean(rememberMeStringValue);
			if (isRemembered) {
				casToken.setRememberMe(true);
			}
			// create simple authentication info
			CtgUser ctgUser = createCtgUser(sysUserCode, sysUserId, staffId);
			
			List<Object> principals = org.apache.shiro.util.CollectionUtils
					.asList(ctgUser, attributes);
			PrincipalCollection principalCollection = new SimplePrincipalCollection(
					principals, getName());
			return new SimpleAuthenticationInfo(principalCollection, ticket);
		} catch (TicketValidationException e) {
			e.printStackTrace();
			throw new CasAuthenticationException("Unable to validate ticket ["
					+ ticket + "]", e);
		} catch (Exception e) {
			e.printStackTrace();
			throw new AuthenticationException();
		}
	}

	private CtgUser createCtgUser(String sysUserCode, Long sysUserId, Long staffId) {
		CtgUser ctgUser = new CtgUser(sysUserCode, sysUserId, staffId);
		if (sysUserId != null) {
			// Long[] sysRoleId =
			// systemRolesService.selectRoleIdsBySysUserId(sysUserId);
			
			Long[] roleIds = getRoleIdsByUId(sysUserId);
			ctgUser.setSysRoleId(roleIds);
			
			Map<String, Object> staffNameMap = permissionService.getStaffNameById(staffId);
			String staffName = (String)staffNameMap.get("STAFF_NAME");
			ctgUser.setStaffName(staffName);
			
			Map<String, Object> rzOrgIdMap = permissionService.getRzOrgIdByStaffId(staffId);
			Long rzOrgId = (Long)rzOrgIdMap.get("ORG_ID");
			
			Map<String, Object> rzOrgCodeMap = permissionService.getRzOrgCodeById(rzOrgId);
			String rzOrgCode = (String)rzOrgCodeMap.get("ORG_CODE");

			List<Map<String, Object>> postList = permissionService
					.getPostByUId(sysUserId);
			
			ctgUser.setRzOrgId(rzOrgId);
			ctgUser.setRzOrgCode(rzOrgCode);
			
			// TODO 单点登录时，赢取机构信息，实际如有多个岗位，则需要界面选择，待完善
			if (postList != null && postList.size() > 0) {
				Map<String, Object> post = postList.get(0);
				ctgUser.setSysPostId((Long) post.get("SYS_POST_ID"));
			}
		}
		return ctgUser;
	}

	private Long[] getRoleIdsByUId(Long sysUserId) {
		List<Map<String, Object>> roleIds = permissionService
				.getRoleByUId(sysUserId);
		Long[] sysRoleIds = new Long[roleIds.size()];
		for (int i = 0; i < sysRoleIds.length; i++) {
			sysRoleIds[i] = (Long) roleIds.get(i).get("SYS_ROLE_ID");
		}
		return sysRoleIds;
	}

}
